﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.DirectoryServices.AccountManagement;
using System.Linq;
using System.Web.Hosting;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using AdGroupMembersWebPart.Classes.Logging;
using Microsoft.SharePoint.WebControls;


namespace AdGroupMembersWebPart.WebParts.AdGroupMembersWebPart
{
    [ToolboxItem(false)]
    public partial class AdGroupMembersWp : WebPart, IWebEditable
    {
        public string ldapQuery { get; set; }
        public string opcije { get; set; }
        public string ou { get; set; }
        private string _errors = "";
        public bool ShowErrors { get; set; }
        public bool UseCustomCssFile { get; set; }
        public string CustomCssFileUrl { get; set; }
        public bool DisplayUserDetails { get; set; }
        public bool DisplayLyncStatus { get; set; }
        public string DisplayLyncStatusAttribute { get; set; }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            InitializeControl();

            CssRegistration cssRegistration = new CssRegistration();
            string cssLink = "";
            if (UseCustomCssFile && !String.IsNullOrEmpty(CustomCssFileUrl))
            {
                cssRegistration.Name = CustomCssFileUrl;
            }
            else
            {
                cssRegistration.Name = string.Format(@"{0}{1}",
               SPContext.Current.Site.Url.EndsWith("/") ? SPContext.Current.Site.Url : SPContext.Current.Site.Url + "/", "Style library/AdGroupMembersWebPart/AdGroupWebPartStyling.css");
            }

            cssRegistration.ID = "groupMembersWebPartCssRegistration";
            cssRegistration.After = "corev15.css";
            Page.Controls.Add(cssRegistration);
        }

        private void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                if (!ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
                {
                    _errors = "";
                    if (opcije == "Display site users")
                    {
                        DisplaySiteUsers(TreeView1, ldapQuery);
                    }
                    else if (opcije == "Display users and groups from specific OU")
                    {
                        if (!string.IsNullOrEmpty(ou))
                        {
                            try
                            {
                                using (HostingEnvironment.Impersonate())
                                {
                                    PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, ldapQuery, ou);
                                    DisplayGroupsFromOu(principalContext, TreeView1, ldapQuery);
                                }
                            }
                            catch (Exception exception)
                            {
                                Logger.ToLog(exception);
                                _errors += "Error querying Active Directory. Check your ldap filter. " + exception.Message;
                                Logger.ToLogEvent(exception, "Error");
                            }
                        }
                        else
                        {
                            try
                            {
                                using (HostingEnvironment.Impersonate())
                                {
                                    PrincipalContext principalContext = new PrincipalContext(ContextType.Domain,
                                                                                             ldapQuery);
                                    DisplayGroupsFromOu(principalContext, TreeView1, ldapQuery);
                                }
                            }
                            catch (Exception exception)
                            {
                                Logger.ToLog(exception);
                                _errors += "Error querying Active Directory. Check your ldap filter. " + exception.Message;
                                Logger.ToLogEvent(exception, "Error");
                            }
                        }
                    }
                }
                if (!string.IsNullOrEmpty(_errors) && ShowErrors)
                {
                    lblErrors.Text = string.Format(@"There were some errors displaying users and groups: <strong>{0}</strong>", _errors);
                }
            }
        }

        /// <summary>
        ///Display users who have permissions on current web
        /// </summary>
        /// <param name="treeView">Tree viev object to populate</param>
        /// <param name="ldapQuery">ldapQuery from web part properties</param>
        private void DisplaySiteUsers(TreeView treeView, string ldapQuery)
        {
            SPWeb web = SPContext.Current.Web;
            SPSite site = SPContext.Current.Site;
            string siteUrl = site.Url;
            SPList userList = site.RootWeb.SiteUserInfoList;
            Guid userListId = userList.ID;
            SPGroupCollection groups = web.Groups;
            SPUserCollection users = web.Users;
            using (HostingEnvironment.Impersonate())
            {
                PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, ldapQuery);
                foreach (SPGroup spGroup in groups)
                {
                    CreateNewTreeNode(spGroup.Name, siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/group.png", spGroup.CanCurrentUserViewMembership ? web.Url + @"/_layouts/userdisp.aspx?id=" + spGroup.ID : "", true, Guid.NewGuid() + ";SpGroup", true, null, spGroup.CanCurrentUserViewMembership ? "_blank" : "", treeView);
                }
                foreach (SPUser spUser in users)
                {
                    try
                    {
                        if (!spUser.IsDomainGroup)
                        {
                            string userIdLink = siteUrl + @"/_layouts/listform.aspx?PageType=4&ListId=" + userListId +
                                                @"&ID=" + spUser.ID;
                            CreateNewTreeNode(GetLyncStatusAttributeValue(principalContext, spUser.LoginName, spUser.Name,userIdLink), siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/user.png", userIdLink, DisplayUserDetails, Guid.NewGuid() + ";user;" + spUser.LoginName, DisplayUserDetails, null, "_blank", treeView);
                        }
                        if (spUser.IsDomainGroup)
                        {
                            CreateNewTreeNode(spUser.Name, siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/Domaingroup.png", siteUrl + @"/_layouts/listform.aspx?PageType=4&ListId=" + userListId + @"&ID=" + spUser.ID, true, Guid.NewGuid() + ";SecurityGroup", true, null, "_blank", treeView);
                        }
                    }
                    catch (Exception exception)
                    {
                        Logger.ToLog(exception);
                        _errors += exception.Message;
                        Logger.ToLogEvent(exception, "Error");
                    }
                }
                principalContext.Dispose();
            }
        }
        /// <summary>
        /// Get attribute value to display Lync status in web part
        /// </summary>
        /// <param name="principalContext"></param>
        /// <param name="userSamAccountName">samAccountName to query AD</param>
        /// <param name="userName">Display name of tree node</param>
        /// <returns></returns>
        private string GetLyncStatusAttributeValue(PrincipalContext principalContext, string userSamAccountName, string userName,string userInfoUrl)
        {
            string nodeDisplayName = "";

            if (DisplayLyncStatus)
            {
                try
                {
                    UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName,
                                userSamAccountName);
                    string lyncStatusLookUpValue = "";
                    if (userPrincipal !=null)
                    {
                        switch (DisplayLyncStatusAttribute)
                        {
                            case "UPN":
                                lyncStatusLookUpValue = userPrincipal.UserPrincipalName;
                                break;
                            case "mail":
                                lyncStatusLookUpValue = userPrincipal.EmailAddress;
                                break;
                        } 
                    }
                    nodeDisplayName = string.Format(@"</a><span class='ms-imnSpan'>
                                                            <a href='#' onclick='IMNImageOnClick(event);return false;' class='ms-imnlink ms-spimn-presenceLink' >
                                                                <span class='ms-spimn-presenceWrapper ms-imnImg ms-spimn-imgSize-10x10'>
                                                                    <img name='imnmark' title='' showofflinepawn='1' class='ms-spimn-img ms-spimn-presence-disconnected-10x10x32' src='/_layouts/15/images/spimn.png?rev=23' alt='User Presence' sip='{0}' id='imn_{1},type=smtp' />
                                                                </span>
                                                            </a>
                                                    </span><a href='{2}'>{3}</a>", lyncStatusLookUpValue, Guid.NewGuid(), String.IsNullOrEmpty(userInfoUrl) ? "#" : userInfoUrl, userName);
                }
                catch (Exception ex)
                {
                    Logger.ToLog(ex);
                }
            }
            else
            {
                nodeDisplayName = userName;
            }
            return nodeDisplayName;
        }

        /// <summary>
        /// Method to populate members of specific group on tree node expand event
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void TreeView1_OnTreeNodePopulate(object sender, TreeNodeEventArgs e)
        {
            using (HostingEnvironment.Impersonate())
            {
                SPSite site = SPContext.Current.Site;
                string siteUrl = site.Url;
                SPWeb web = SPContext.Current.Web;
                Guid userListId = site.RootWeb.SiteUserInfoList.ID;
                TreeNode maiNode = e.Node;
                if (maiNode.Value.EndsWith("SpGroup"))
                {
                    try
                    {
                        GetMembersOfSharePointGroup(maiNode.Text, web, maiNode, userListId);
                    }
                    catch (Exception ex)
                    {
                        Logger.ToLog(ex);
                    }
                }
                else if (maiNode.Value.EndsWith("SecurityGroup"))
                {
                    try
                    {
                        PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, ldapQuery);
                        if (opcije != "Display users and groups from specific OU")
                        {
                            try
                            {
                                GetMembersOfAdGroup(maiNode.Text, principalContext, maiNode, siteUrl, web,
                                                    userListId);
                            }
                            catch (Exception ex)
                            {
                                Logger.ToLog(ex);
                            }
                        }
                        else
                        {
                            try
                            {
                                GetGroupMembersFromSpecificOu(maiNode.Text, maiNode, web.Site.Url, ldapQuery);
                            }
                            catch (Exception ex)
                            {
                                Logger.ToLog(ex);
                            }
                        }
                        principalContext.Dispose();
                    }
                    catch (Exception ex)
                    {
                        Logger.ToLog(ex);
                    }
                }
                else if (DisplayUserDetails && maiNode.Value.Contains(";user;"))
                {
                    PrincipalContext principalContext = new PrincipalContext(ContextType.Domain);
                    try
                    {
                        string[] values = maiNode.Value.Split((new char[] { ';' }), StringSplitOptions.RemoveEmptyEntries);

                        UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext,
                            IdentityType.SamAccountName, values.Last());
                        if (userPrincipal != null)
                        {
                            CreateNewTreeNode(string.Format(@"Email: <a href='mailto:{0}'>{0}</a>", userPrincipal.EmailAddress), "", "", false,
                                "", false, maiNode, "", null);
                            CreateNewTreeNode(string.Format(@"Phone: {0}", userPrincipal.VoiceTelephoneNumber), "", "",
                                false, "", false, maiNode, "", null);
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.ToLog(ex);
                    }
                    finally
                    {
                        principalContext.Dispose();
                    }
                }
            }
        }
        /// <summary>
        /// Helper method to get members of SharePoint group
        /// </summary>
        /// <param name="groupName">Name of SP group</param>
        /// <param name="web">Current web</param>
        /// <param name="treeNode">TreeNode object</param>
        /// <param name="userListId">Guid of User Information list</param>
        private void GetMembersOfSharePointGroup(string groupName, SPWeb web, TreeNode treeNode, Guid userListId)
        {
            SPGroup spGroup = web.SiteGroups[groupName];
            if (spGroup != null)
            {
                string siteUrl = web.Site.Url;
                if (spGroup.CanCurrentUserViewMembership)
                {
                    try
                    {
                        foreach (SPUser spUser in spGroup.Users)
                        {
                            if (!spUser.IsDomainGroup)
                            {
                                using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain))
                                {
                                    string userIdUrl = siteUrl + @"/_layouts/listform.aspx?PageType=4&ListId=" + userListId + @"&ID=" + spUser.ID;
                                    CreateNewTreeNode(GetLyncStatusAttributeValue(principalContext, spUser.LoginName, spUser.Name, userIdUrl), siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/user.png", userIdUrl, DisplayUserDetails, Guid.NewGuid() + ";user;" + spUser.LoginName, DisplayUserDetails, treeNode, "_blank", null);
                                }

                            }
                            else if (spUser.IsDomainGroup)
                            {
                                CreateNewTreeNode(spUser.Name, siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/Domaingroup.png", siteUrl + @"/_layouts/listform.aspx?PageType=4&ListId=" + userListId + @"&ID=" + spUser.ID, true, Guid.NewGuid() + ";SecurityGroup", true, treeNode, "_blank", null);
                            }
                        }
                    }
                    catch (Exception exception)
                    {
                        Logger.ToLog(exception);
                        _errors += exception.Message + Environment.NewLine;
                        Logger.ToLogEvent(exception, "Error");
                    }
                }
            }
        }
        /// <summary>
        /// Helper method to call members of security groups recursively
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="principalContext"></param>
        /// <param name="treeNode"></param>
        /// <param name="siteUrl"></param>
        /// <param name="web"></param>
        /// <param name="userListId"></param>
        private void GetMembersOfAdGroup(string groupName, PrincipalContext principalContext, TreeNode treeNode, string siteUrl, SPWeb web, Guid userListId)
        {
            SPUserCollection users = web.AllUsers;
            GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, groupName);
            if (groupPrincipal != null)
            {
                try
                {
                    foreach (Principal principal in groupPrincipal.GetMembers(false))
                    {
                        if (principal.StructuralObjectClass.ToLower() == "user")
                        {
                            try
                            {
                                UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, principal.SamAccountName);
                                if (user != null)
                                {
                                    string navigateUrl = "";
                                    try
                                    {
                                        if (!string.IsNullOrEmpty(user.EmailAddress))
                                        {
                                            string userEmail = user.EmailAddress;
                                            if (users.Cast<SPUser>().FirstOrDefault(u => String.Equals(u.Email, userEmail, StringComparison.CurrentCultureIgnoreCase)) != null)
                                            {
                                                navigateUrl =
                                                    string.Format(
                                                        @"{0}/_layouts/listform.aspx?PageType=4&ListId={1}&ID={2}",
                                                        siteUrl, userListId, users.GetByEmail(userEmail).ID);
                                            }
                                        }
                                    }
                                    catch (Exception exception)
                                    {
                                        Logger.ToLog(exception);
                                        navigateUrl = "";
                                        _errors += exception.Message + " " + user.Name + " " + Environment.NewLine;
                                    }
                                    finally
                                    {
                                        CreateNewTreeNode(GetLyncStatusAttributeValue(principalContext, user.SamAccountName, String.IsNullOrEmpty(user.DisplayName ) ? user.Name : user.DisplayName,navigateUrl), siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/user.png", navigateUrl, DisplayUserDetails, Guid.NewGuid() + ";user;" + user.SamAccountName, DisplayUserDetails, treeNode, navigateUrl != "" ? "_blank" : "", null);
                                        user.Dispose();
                                    }
                                }
                            }
                            catch (Exception exception)
                            {
                                Logger.ToLog(exception);
                                _errors += exception.Message;
                                CreateNewTreeNode(GetLyncStatusAttributeValue(principalContext, principal.SamAccountName, String.IsNullOrEmpty(principal.DisplayName)? principal.Name : principal.DisplayName,""), siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/user.png", "", DisplayUserDetails, Guid.NewGuid() + ";user;" + principal.SamAccountName, DisplayUserDetails, treeNode, "", null);
                            }
                        }
                        else
                        {
                            CreateNewTreeNode(principal.Name, siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/Domaingroup.png", "", true, Guid.NewGuid() + ";SecurityGroup", true, treeNode, "", null);
                        }
                    }
                }
                catch (Exception exception)
                {
                    Logger.ToLog(exception);
                    _errors += exception.Message;
                    Logger.ToLogEvent(exception, "Error");
                }
                groupPrincipal.Dispose();
            }
        }
        /// <summary>
        /// Create new tree node in tree view control
        /// </summary>
        /// <param name="name">Name of the node</param>
        /// <param name="imageUrl">url to object icon</param>
        /// <param name="navigateUrl">url to user information list</param>
        /// <param name="populateOnDemand">enables expanding object</param>
        /// <param name="value">custom value</param>
        /// <param name="expand">tree node select action</param>
        /// <param name="parenTreeNode">parent tree node or null if adding to root of tree view</param>
        /// <param name="target">if you want opening in links in new tab</param>
        /// <param name="treeView">if adding to root of treeview control</param>
        private void CreateNewTreeNode(string name, string imageUrl, string navigateUrl, bool populateOnDemand, string value, bool expand, TreeNode parenTreeNode, string target, TreeView treeView)
        {
            TreeNode treeNode = new TreeNode(name);
            treeNode.ImageUrl = imageUrl;
            treeNode.NavigateUrl = navigateUrl;
            treeNode.Target = target;
            treeNode.PopulateOnDemand = populateOnDemand;
            treeNode.Value = value;
            if (expand)
            {
                treeNode.SelectAction = TreeNodeSelectAction.Expand;
            }
            if (parenTreeNode == null)
            {
                treeView.Nodes.Add(treeNode);
            }
            else
            {
                parenTreeNode.ChildNodes.Add(treeNode);
            }
        }

        /// <summary>
        /// Display users and groups from specific OU
        /// </summary>
        /// <param name="principalContext"></param>
        /// <param name="treeView"></param>
        /// <param name="ldapQuery"></param>
        private void DisplayGroupsFromOu(PrincipalContext principalContext, TreeView treeView, string ldapQuery)
        {
            SPSite site = SPContext.Current.Site;
            string siteUrl = site.Url;
            UserPrincipal userPrincipal = new UserPrincipal(principalContext);
            userPrincipal.Enabled = true;
            PrincipalSearcher principalSearcher = new PrincipalSearcher(userPrincipal);
            PrincipalSearchResult<Principal> userResults = principalSearcher.FindAll();

            if (userResults != null)
            {
                foreach (UserPrincipal userResult in userResults)
                {
                    CreateNewTreeNode(GetLyncStatusAttributeValue(principalContext, userResult.SamAccountName, String.IsNullOrEmpty(userResult.DisplayName) ? userResult.Name : userResult.DisplayName,""), siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/user.png", "", DisplayUserDetails, Guid.NewGuid() + ";user;" + userResult.SamAccountName, DisplayUserDetails, null, "", treeView);
                }
            }
            principalSearcher.Dispose();
            userPrincipal.Dispose();

            GroupPrincipal groupPrincipal = new GroupPrincipal(principalContext);
            groupPrincipal.IsSecurityGroup = true;
            PrincipalSearcher groupPrincipalSearcher = new PrincipalSearcher(groupPrincipal);
            PrincipalSearchResult<Principal> groupResults = groupPrincipalSearcher.FindAll();
            if (groupResults != null)
            {
                foreach (GroupPrincipal groupResult in groupResults)
                {
                    CreateNewTreeNode(groupResult.Name, siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/Domaingroup.png", "", true, Guid.NewGuid() + ";SecurityGroup", true, null, "", treeView);
                }
            }
            groupPrincipalSearcher.Dispose();
            groupPrincipal.Dispose();
        }

        /// <summary>
        /// Get members of groups in specific OU
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="treeNode"></param>
        /// <param name="siteUrl"></param>
        /// <param name="ldapQuery"></param>
        private void GetGroupMembersFromSpecificOu(string groupName, TreeNode treeNode, string siteUrl, string ldapQuery)
        {
            using (HostingEnvironment.Impersonate())
            {
                PrincipalContext principalContext = new PrincipalContext(ContextType.Domain, ldapQuery);
                GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(principalContext,
                                                                              IdentityType.SamAccountName, groupName);
                if (groupPrincipal != null)
                {
                    foreach (Principal principal in groupPrincipal.GetMembers(false))
                    {
                        if (principal.StructuralObjectClass.ToLower() == "user")
                        {
                            UserPrincipal user = UserPrincipal.FindByIdentity(principalContext, principal.SamAccountName);
                            if (user != null)
                            {
                                CreateNewTreeNode(GetLyncStatusAttributeValue(principalContext, user.SamAccountName, String.IsNullOrEmpty(user.DisplayName) ? user.Name : user.DisplayName,""), siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/user.png", "", DisplayUserDetails, Guid.NewGuid() + ";user;" + principal.SamAccountName, DisplayUserDetails, treeNode, "", null);
                                user.Dispose();
                            }
                        }
                        else
                        {
                            CreateNewTreeNode(principal.Name, siteUrl + @"/_layouts/15/images/ADGroupMembersWebPart/Domaingroup.png", "", true, Guid.NewGuid() + ";SecurityGroup", true, treeNode, "", null);
                        }
                    }
                    groupPrincipal.Dispose();
                    principalContext.Dispose();
                }
            }
        }

        protected void groupMembersButtonCollapseAll_OnClick(object sender, EventArgs e)
        {
            TreeView1.CollapseAll();
        }

        protected void groupMembersButtonExpandAll_OnClick(object sender, EventArgs e)
        {
            TreeView1.ExpandAll();
        }

        EditorPartCollection IWebEditable.CreateEditorParts()
        {
            List<EditorPart> editors = new List<EditorPart>();
            SkupineEditor editorPart = new SkupineEditor();
            editorPart.ID = this.ID + "_editorPart";
            editors.Add(editorPart);
            return new EditorPartCollection(editors);
        }

        object IWebEditable.WebBrowsableObject
        {
            get
            {
                return this;
            }
        }


    }
}
